Extracción de características radiómicas

Colab
Fecha de publicación

14 de febrero de 2025

Colab

Open In Colab

En este notebook, te mostraremos cómo extraer características en modo batch utilizando el enfoque de la interfaz de línea de comandos (cli).

Utilizaremos datos del repositorio https://github.com/rcuocolo/PROSTATEx_masks

Primero, necesitamos verificar e instalar las bibliotecas necesarias para realizar estos pasos más fácilmente.

!pip install --progress-bar off -q gdown
!pip install --upgrade --pre SimpleITK --find-links https://github.com/SimpleITK/SimpleITK/releases/tag/latest --progress-bar off -q
!pip install --progress-bar off -q matplotlib
!pip install --progress-bar off -q pyradiomics
!pip install --progress-bar off -q pyyaml
!pip install --progress-bar off -q pandas
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  Preparing metadata (pyproject.toml) ... done
  Preparing metadata (setup.py) ... done
  Preparing metadata (setup.py) ... done
  Building wheel for pyradiomics (setup.py) ... done
  Building wheel for docopt (setup.py) ... done

Importar librerías necesarias

import os
import pandas as pd
import csv
import yaml
import SimpleITK as sitk
import radiomics
import matplotlib.pyplot as plt
import gdown
import random
import numpy as np
import ipywidgets as widgets
from ipywidgets import interact, fixed, Layout, Button, HBox
from IPython.display import display, clear_output
import seaborn as sns
from sklearn.feature_selection import VarianceThreshold
# prompt: Montar mi unidad de google drive

from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive

Descargar imágenes ProstateX y archivo de extracción de máscaras y parámetros

import os
if not os.path.isdir('input_t2w.csv'):
    !gdown 1dNqdVsN0PlCG01sl6wUCh9QJ5NnkOrDS -q
    !unzip lesions_T2.zip >/dev/null
    !rm lesions_T2.zip >/dev/null
    !rm -r __MACOSX >/dev/null
else:
    print("lesions_T2 folder already exists, the zip was not downloaded")

if not os.path.isfile('Params_T2_BW.yaml'):
    !gdown 12VOQCPFK4HARrwQzbB4fr_f2PepGPFWm -q
else:
    print("Params_T2_BW.yaml already exists, the file was not downloaded")

print('Done!')
Done!

Visualizar una imagen al azar y la máscara de la lesión

# Rutas a las carpetas (relativas para Colab)
ruta_imagenes = '/content/lesions_T2/Images'  # Usamos rutas relativas para Colab
ruta_mascaras = '/content/lesions_T2/Masks' # Usamos rutas relativas para colab

# Variables globales
pareja_actual = None
parejas_archivos = []
output_widget = widgets.Output()  # Widget de salida para mostrar las imágenes

def mostrar_imagen_y_mascara(ruta_imagen, ruta_mascara, zoom_x=0, zoom_y=0, zoom_size=50):
    """
    Muestra las tres imágenes (MRI, MRI+máscara, MRI+máscara con zoom).
    """
    try:
        imagen_sitk = sitk.ReadImage(ruta_imagen)
        mascara_sitk = sitk.ReadImage(ruta_mascara)
    except RuntimeError as e:
        print(f"Error al leer: {e}")
        return

    imagen_array = sitk.GetArrayFromImage(imagen_sitk)
    mascara_array = sitk.GetArrayFromImage(mascara_sitk)

    if imagen_array.shape != mascara_array.shape:
        if len(imagen_array.shape) == 3 and len(mascara_array.shape) == 4:
            if mascara_array.shape[3] == 1:
                mascara_array = np.squeeze(mascara_array, axis=3)
            else:
                print("Dimensiones incompatibles (4D).")
                return
        elif len(imagen_array.shape) != 3 or len(mascara_array.shape) != 3:
            print("Dimensiones incompatibles.")
            return

    cortes_con_lesion = np.where(np.any(mascara_array != 0, axis=(1, 2)))[0]
    if cortes_con_lesion.size == 0:
        print("No se encontraron cortes con lesión.")
        if len(imagen_array.shape) == 3:
            corte = random.randint(0, imagen_array.shape[0] - 1)
        else:
            imagen_slice = imagen_array
            mascara_slice = mascara_array
    else:
        corte = random.choice(cortes_con_lesion)

    if len(imagen_array.shape) == 3:
      imagen_slice = imagen_array[corte, :, :]
      mascara_slice = mascara_array[corte, :, :]

    # --- Preparación para el zoom ---
    zoom_x = max(zoom_size, min(zoom_x, imagen_slice.shape[1] - zoom_size - 1))
    zoom_y = max(zoom_size, min(zoom_y, imagen_slice.shape[0] - zoom_size - 1))
    fila_inicio = max(0, zoom_y - zoom_size)
    fila_fin = min(imagen_slice.shape[0], zoom_y + zoom_size)
    col_inicio = max(0, zoom_x - zoom_size)
    col_fin = min(imagen_slice.shape[1], zoom_x + zoom_size)
    imagen_zoom = imagen_slice[fila_inicio:fila_fin, col_inicio:col_fin]
    mascara_zoom = mascara_slice[fila_inicio:fila_fin, col_inicio:col_fin]
    mascara_zoom_rgba = np.zeros((mascara_zoom.shape[0], mascara_zoom.shape[1], 4))
    mascara_zoom_rgba[mascara_zoom != 0, :3] = plt.cm.jet(mascara_zoom[mascara_zoom != 0])[:, :3]
    mascara_zoom_rgba[mascara_zoom != 0, 3] = 0.5

    # --- Creación de la figura y subplots DENTRO del contexto del widget de salida ---
    with output_widget:
        clear_output(wait=True)  # Borrar la salida anterior
        fig, axes = plt.subplots(1, 3, figsize=(18, 6))

        # --- Mostrar imagen original (sin zoom) ---
        axes[0].imshow(imagen_slice, cmap='gray')
        axes[0].set_title(f'Imagen MRI (Corte: {corte})')
        axes[0].axis('off')

        # --- Mostrar imagen con máscara superpuesta (sin zoom) ---
        mascara_rgba = np.zeros((mascara_slice.shape[0], mascara_slice.shape[1], 4))
        mascara_rgba[mascara_slice != 0, :3] = plt.cm.jet(mascara_slice[mascara_slice != 0])[:, :3]
        mascara_rgba[mascara_slice != 0, 3] = 0.5
        axes[1].imshow(imagen_slice, cmap='gray')
        axes[1].imshow(mascara_rgba)
        axes[1].set_title(f'Máscara Superpuesta (Corte: {corte})')
        axes[1].axis('off')

        # --- Mostrar imagen con máscara superpuesta (zoom) ---
        axes[2].imshow(imagen_zoom, cmap='gray')
        axes[2].imshow(mascara_zoom_rgba)
        axes[2].set_title('Máscara Superpuesta (Zoom)')
        axes[2].axis('off')

        plt.tight_layout()
        plt.show()


def obtener_parejas_archivos(ruta_imagenes, ruta_mascaras):
    """Obtiene y empareja archivos."""
    imagenes = sorted([f for f in os.listdir(ruta_imagenes) if f.endswith('.nii.gz') and "ProstateX" in f])
    mascaras = sorted([f for f in os.listdir(ruta_mascaras) if f.endswith('.nii.gz') and "ProstateX" in f])
    parejas = []
    for img in imagenes:
        img_id = img.split('_')[0]
        for masc in mascaras:
            if img_id in masc:
                ruta_img = os.path.join(ruta_imagenes, img)
                ruta_masc = os.path.join(ruta_mascaras, masc)
                parejas.append((ruta_img, ruta_masc))
                break  # Importante: salir del bucle interno una vez encontrada la pareja
    return parejas



def actualizar_imagen(b):
    """Función para actualizar la imagen al hacer clic en el botón."""
    global pareja_actual, parejas_archivos
    if not parejas_archivos:
        parejas_archivos = obtener_parejas_archivos(ruta_imagenes, ruta_mascaras)
    pareja_actual = random.choice(parejas_archivos)
    imagen_sitk = sitk.ReadImage(pareja_actual[0])
    imagen_array = sitk.GetArrayFromImage(imagen_sitk)

    if len(imagen_array.shape) == 3:
        dim_x = imagen_array.shape[2]
        dim_y = imagen_array.shape[1]
    else:
        dim_x = imagen_array.shape[1]
        dim_y = imagen_array.shape[0]

    zoom_x_slider.max = dim_x - 1
    zoom_x_slider.value = dim_x // 2
    zoom_y_slider.max = dim_y - 1
    zoom_y_slider.value = dim_y // 2
    zoom_size_slider.max = min(dim_x, dim_y) // 2
    zoom_size_slider.value = 50

    mostrar_imagen_y_mascara(pareja_actual[0], pareja_actual[1],
                            zoom_x_slider.value, zoom_y_slider.value, zoom_size_slider.value)


# --- Configuración inicial ---
parejas_archivos = obtener_parejas_archivos(ruta_imagenes, ruta_mascaras)
if not parejas_archivos:
    print("No se encontraron parejas de archivos.")
    #Si no se encuentran parejas, no se puede continuar. Se podría poner un mensaje en un widget.
else:
    pareja_actual = random.choice(parejas_archivos)
    imagen_sitk_inicial = sitk.ReadImage(pareja_actual[0])
    imagen_array_inicial = sitk.GetArrayFromImage(imagen_sitk_inicial)

    if len(imagen_array_inicial.shape) == 3:
        dim_x = imagen_array_inicial.shape[2]
        dim_y = imagen_array_inicial.shape[1]
    else: #Si es 2D
        dim_x = imagen_array_inicial.shape[1]
        dim_y = imagen_array_inicial.shape[0]


    # --- Crear widgets ---
    zoom_x_slider = widgets.IntSlider(min=0, max=dim_x - 1, step=1, value=dim_x // 2, description='Zoom X', continuous_update=False, layout=Layout(width='50%'))
    zoom_y_slider = widgets.IntSlider(min=0, max=dim_y - 1, step=1, value=dim_y // 2, description='Zoom Y', continuous_update=False, layout=Layout(width='50%'))
    zoom_size_slider = widgets.IntSlider(min=10, max=min(dim_x,dim_y) // 2, step=1, value=50, description='Zoom Size', continuous_update=False, layout=Layout(width='50%'))
    boton_cambiar = Button(description="Cambiar Imagen")
    boton_cambiar.on_click(actualizar_imagen)

    # --- Configurar la interfaz ---
    ui = HBox([boton_cambiar, zoom_x_slider, zoom_y_slider, zoom_size_slider])
    # Ya no usamos interactive_output, sino el widget de salida directamente
    display(ui, output_widget)

    # Mostrar la imagen inicial
    mostrar_imagen_y_mascara(pareja_actual[0], pareja_actual[1],
                            zoom_x_slider.value, zoom_y_slider.value, zoom_size_slider.value)

Veamos nuestro archivo de parámetros

Seleccione el icono de la carpeta en la barra de la izquierda y haga doble clic en Params_T2_BW.yaml para verlo.

Normalización

Dado que estamos trabajando con imágenes T2w MRI vamos a normalizar las imágenes utilizando el parámetro normalize que establecemos como true. Esto fija la intensidad media de las imágenes en cero y escala la desviación estándar para que sea igual a uno. A continuación escalamos la desviación estándar de las intensidades de las imágenes a 100 estableciendo normalizeScale en true.

El cálculo de algunas características (a saber, la energía, la energía total y RMS) se ve afectada por la existencia de valor negativo y ya que hemos normalizado nuestras imágenes para tener media 0 y desviación estándar de 100 tenemos que asegurarnos de que la mayoría de nuestras intensidades de imagen normalizada es igual o superior a 0. Podemos hacer esto usando el voxelArrayShift. Seleccionamos un valor de 300, que asumiendo una distribución normal de las intensidades de la imagen aseguraría que el 99.7% de ellas están por encima de 0.

Ahorro de memoria

El valor preCrop ajustado a true nos permite ahorrar algo de memoria durante el cálculo de las características (y posiblemente evitar errores).

Tipo de extracción de características (2D vs 3D)

Tenemos un conjunto de datos en el que nuestras imágenes son anisótropas (las dimensiones de los vóxeles no son las mismas para los tres ejes) y, más concretamente, el grosor del corte es mucho mayor que la resolución en el plano. Debido a esto, realizaremos la extracción de características radiómicas 2D en lugar de 3D. Ya que nuestras imágenes son Axiales T2w, necesitamos definir que deseamos realizar una extracción de características 2D estableciendo force2D a true y estableciendo el eje en el plano a 0 usando force2Ddimension (Axial - 0, Coronal - 1, Sagital - 2).

Interpolación/Remuestreo

Para asegurar que estamos extrayendo características radiómicas que son comparables entre diferentes imágenes necesitamos estandarizar las dimensiones de los vóxeles en el plano. Hacemos esto ajustando el interpolator a sitkBSpline y el resampledPixelSpacing a una resolución elegida [0.6, 0.6, 0] (el 0 en la tercera posición significa que no remuestrearemos el grosor del corte y sólo la resolución en el plano de las imágenes será transformada a 0.6 mm x 0.6 mm, lo cual está bien porque las características serán extraídas en 2D).

Resegmentación

La resegmentación permite redefinir la máscara para, por ejemplo, descartar intensidades en nuestra máscara que estén fuera de \(mean\ \pm\ 3\times std\). Podemos hacerlo ajustando resegmentMode a sigma y resegmentRange a [-3, 3].

Discritización de la intensidad

Para asegurarnos de que el número de bins utilizados para la extracción de características está entre 30 y 130, realizamos una primera extracción de características para obtener el original_firstorder_Range de nuestros pacientes y seleccionamos el binWidth para que sea mayor que 30 y menor que 130 en todos los pacientes.

Filtros y características

Utilizamos imageType para definir los tipos de imágenes de las que queremos extraer las características radiómicas y featureClass para seleccionar el tipo de características a extraer.

Realizar la extracción de características radiómicas - Crear el fichero de entrada

Para la extracción de características radiómicas utilizaremos el modo batch de PyRadiomics, donde tendremos que proporcionar como entrada el fichero .csv que contiene 3 columnas. La primera columna es el ID, la segunda la Image y la tercera la Mask, como se muestra a continuación.

if not os.path.isfile('input_t2w.csv'):
    dir_image_path = 'lesions_T2/Images'
    dir_mask_path = 'lesions_T2/Masks'

    img_files = sorted([os.path.join(dir_image_path, x) for x in os.listdir(dir_image_path) if '.nii' in x])
    msk_files = sorted([os.path.join(dir_mask_path, x) for x in os.listdir(dir_mask_path) if '.nii.gz' in x])

    input_csv_t2 = [["ID", "Image", "Mask"]]
    for msk_file in msk_files:
        patient_id = os.path.basename(msk_file)[:14]
        image = [x for x in img_files if patient_id in x][0]
        input_csv_t2.append([patient_id, image, msk_file])

    with open('input_t2w.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerows(input_csv_t2)

Comprobar el fichero de entrada

input_df = pd.read_csv('input_t2w.csv')
input_df.head()
ID Image Mask
0 ProstateX-0000 lesions_T2/Images/ProstateX-0000_t2_tse_tra_4.... lesions_T2/Masks/ProstateX-0000-Finding1-t2_ts...
1 ProstateX-0001 lesions_T2/Images/ProstateX-0001_t2_tse_tra_10... lesions_T2/Masks/ProstateX-0001-Finding1-t2_ts...
2 ProstateX-0002 lesions_T2/Images/ProstateX-0002_t2_tse_tra_4.... lesions_T2/Masks/ProstateX-0002-Finding1-t2_ts...
3 ProstateX-0002 lesions_T2/Images/ProstateX-0002_t2_tse_tra_4.... lesions_T2/Masks/ProstateX-0002-Finding2-t2_ts...
4 ProstateX-0003 lesions_T2/Images/ProstateX-0003_t2_tse_tra_3.... lesions_T2/Masks/ProstateX-0003-Finding1-t2_ts...
# prompt: crear una tabla simplificada de input_df tomando solamente 20 registros al azar y guardarla como csv con el mismo formato que input_t2w.csv



# Lee el archivo CSV de entrada
input_df = pd.read_csv('input_t2w.csv')

# Muestra las primeras 5 filas del DataFrame para verificar
print(input_df.head())

# Selecciona 2 filas aleatorias
simplified_df = input_df.sample(n=2)

# Guarda el DataFrame simplificado como un nuevo archivo CSV
simplified_df.to_csv('simplified_input.csv', index=False) # index=False evita guardar el índice

print("Archivo 'simplified_input.csv' creado con éxito.")
               ID                                              Image  \
0  ProstateX-0000  lesions_T2/Images/ProstateX-0000_t2_tse_tra_4....   
1  ProstateX-0001  lesions_T2/Images/ProstateX-0001_t2_tse_tra_10...   
2  ProstateX-0002  lesions_T2/Images/ProstateX-0002_t2_tse_tra_4....   
3  ProstateX-0002  lesions_T2/Images/ProstateX-0002_t2_tse_tra_4....   
4  ProstateX-0003  lesions_T2/Images/ProstateX-0003_t2_tse_tra_3....   

                                                Mask  
0  lesions_T2/Masks/ProstateX-0000-Finding1-t2_ts...  
1  lesions_T2/Masks/ProstateX-0001-Finding1-t2_ts...  
2  lesions_T2/Masks/ProstateX-0002-Finding1-t2_ts...  
3  lesions_T2/Masks/ProstateX-0002-Finding2-t2_ts...  
4  lesions_T2/Masks/ProstateX-0003-Finding1-t2_ts...  
Archivo 'simplified_input.csv' creado con éxito.

Entradas necesarias para la ejecución por lotes de PyRadiomics

Además del fichero de entrada tendremos que proporcionar la ruta del fichero de salida donde se guardarán los valores de las características de las diferentes imágenes (usar -o antes de indicar la ruta), definir que queremos la salida como un fichero csv añadiendo -f csv, y finalmente indicar la ruta del fichero de parámetros usando -p.

Nuestro comando tiene el siguiente aspecto

pyradiomics input_t2w.csv -o output_t2w.csv -f csv -p Params_T2_BW.yaml.

Ejecutar la extracción por lotes de características radiómicas

Se aconseja ejecutar la siguiente línea en su terminal o línea de comandos para acelerar la extracción. Para ello, elimine el «!» inicial de la siguiente línea

!pyradiomics input_t2w.csv -o output_t2w.csv -f csv -p Params_T2_BW.yaml
#!pyradiomics simplified_input.csv -o output_t2w.csv -f csv -p Params_T2_BW.yaml
[2025-02-14 20:57:16] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:16] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:16] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:17] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:17] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:18] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:18] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:19] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:19] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:19] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:21] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:21] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:23] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:24] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:24] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:28] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:29] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:30] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:31] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:32] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:35] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:36] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:40] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:41] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:57:42] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:58:10] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:58:10] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:58:21] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:58:35] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated
[2025-02-14 20:58:35] W: radiomics.glcm: GLCM is symmetrical, therefore Sum Average = 2 * Joint Average, only 1 needs to be calculated

Comprobemos el fichero de salida radiomics

Como se puede ver, contiene las mismas tres columnas (ID, Imagen, Máscara) de nuestra entrada, diez columnas que proporcionan información sobre las versiones, las propiedades de la imagen y los ajustes de extracción de características, seguidas de los valores de las características radiómicas extraídas.

# prompt: Grabar el fichero en drive con la ruta output_file_path

import shutil

# ... (rest of your code)

# Assuming you have already mounted your Google Drive
repo_path = '/content/drive/MyDrive/radiomics/' # Replace with the correct path
output_file_path = os.path.join(repo_path, 'output_t2w.csv')

# Use shutil to copy the file. This is more robust than relying on shell commands.
try:
  shutil.copyfile('output_t2w.csv', output_file_path)
  print(f"File 'output_t2w.csv' copied successfully to {output_file_path}")
except FileNotFoundError:
  print("Error: 'output_t2w.csv' not found.  Ensure the radiomics extraction completed successfully.")
except Exception as e:
  print(f"An error occurred: {e}")
File 'output_t2w.csv' copied successfully to /content/drive/MyDrive/radiomics/output_t2w.csv
radiomics_df = pd.read_csv('output_t2w.csv')
#radiomics_df.head()
radiomics_df
ID Image Mask diagnostics_Versions_PyRadiomics diagnostics_Versions_Numpy diagnostics_Versions_SimpleITK diagnostics_Versions_PyWavelet diagnostics_Versions_Python diagnostics_Configuration_Settings diagnostics_Configuration_EnabledImageTypes ... exponential_gldm_GrayLevelNonUniformity exponential_gldm_GrayLevelVariance exponential_gldm_HighGrayLevelEmphasis exponential_gldm_LargeDependenceEmphasis exponential_gldm_LargeDependenceHighGrayLevelEmphasis exponential_gldm_LargeDependenceLowGrayLevelEmphasis exponential_gldm_LowGrayLevelEmphasis exponential_gldm_SmallDependenceEmphasis exponential_gldm_SmallDependenceHighGrayLevelEmphasis exponential_gldm_SmallDependenceLowGrayLevelEmphasis
0 ProstateX-0155 /content/lesions_T2/Images/ProstateX-0155_t2_t... /content/lesions_T2/Masks/ProstateX-0155-Findi... v3.0.1 1.26.4 2.5.0.dev98 1.8.0 3.11.11 {'minimumROIDimensions': 2, 'minimumROISize': ... {'Original': {}, 'LoG': {'sigma': [0.6, 2, 3]}... ... 17.706587 25.164545 63.574850 4.592814 137.802395 0.417549 0.088575 0.597535 54.230289 0.050666
1 ProstateX-0070 /content/lesions_T2/Images/ProstateX-0070_t2_t... /content/lesions_T2/Masks/ProstateX-0070-Findi... v3.0.1 1.26.4 2.5.0.dev98 1.8.0 3.11.11 {'minimumROIDimensions': 2, 'minimumROISize': ... {'Original': {}, 'LoG': {'sigma': [0.6, 2, 3]}... ... 290.531686 1.559546 13.603652 17.186896 176.383459 2.007972 0.111175 0.212818 4.671583 0.020689

2 rows × 1449 columns

# prompt: De la tabla anterior, hacer un gráfico de correlación de los valores que son numéricos

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.feature_selection import VarianceThreshold

radiomics_df = pd.read_csv('output_t2w.csv')

for col in radiomics_df.select_dtypes(include=['object']).columns:
    try:
        radiomics_df[col] = pd.to_numeric(radiomics_df[col], errors='coerce')
    except (ValueError, TypeError):
        pass

numeric_cols = radiomics_df.select_dtypes(include=['number']).dropna(axis=1, how='all')

if numeric_cols.empty:
    print("No numeric columns")
else:
    # --- Filtrado por varianza ---
    selector = VarianceThreshold(threshold=0.01)  # Elimina características con varianza < 0.01.  ¡Ajusta este umbral!
    selected_features = selector.fit_transform(numeric_cols)
    selected_cols = numeric_cols.columns[selector.get_support()]  # Obtiene los nombres de las columnas seleccionadas
    filtered_df = pd.DataFrame(selected_features, columns=selected_cols)
    output_file_path = os.path.join(repo_path, 'coor_file.csv')
    filtered_df.to_csv(output_file_path, index=False)
    # --- Ahora calcula la correlación y grafica SOLO con las características filtradas ---
    correlation_matrix = filtered_df.corr()
    output_file_path = os.path.join(repo_path, 'correlation_matrix.csv')
    correlation_matrix.to_csv(output_file_path, index=False)
# prompt: cargar un archivo csv con pandas

import pandas as pd

# Assuming 'input_t2w.csv' is in your current Colab environment
try:
    correlation_matrix = pd.read_csv('correlation.csv')
    print(correlation_matrix.head())  # Display the first few rows
except FileNotFoundError:
    print("Error: 'input_t2w.csv' not found. Please upload the file or check the file path.")
except pd.errors.EmptyDataError:
    print("Error: 'input_t2w.csv' is empty.")
except pd.errors.ParserError:
    print("Error: Could not parse 'input_t2w.csv'. Check file format.")
except Exception as e:
    print(f"An unexpected error occurred: {e}")
   diagnostics_Image-original_Mean  diagnostics_Image-original_Maximum  \
0                         1.000000                            0.612557   
1                         0.612557                            1.000000   
2                         0.041642                            0.031980   
3                         0.033786                            0.034701   
4                        -0.089001                            0.052747   

   diagnostics_Mask-original_VoxelNum  diagnostics_Mask-original_VolumeNum  \
0                            0.041642                             0.033786   
1                            0.031980                             0.034701   
2                            1.000000                             0.139999   
3                            0.139999                             1.000000   
4                           -0.048258                             0.072818   

   diagnostics_Image-interpolated_Mean  \
0                            -0.089001   
1                             0.052747   
2                            -0.048258   
3                             0.072818   
4                             1.000000   

   diagnostics_Image-interpolated_Minimum  \
0                               -0.310491   
1                                0.131748   
2                               -0.221773   
3                               -0.058360   
4                                0.244601   

   diagnostics_Image-interpolated_Maximum  \
0                               -0.070286   
1                               -0.111855   
2                                0.328244   
3                                0.139916   
4                                0.444568   

   diagnostics_Mask-interpolated_VoxelNum  \
0                                0.037425   
1                                0.023213   
2                                0.975903   
3                                0.137532   
4                               -0.043780   

   diagnostics_Mask-interpolated_VolumeNum  \
0                                 0.008338   
1                                 0.020969   
2                                 0.125609   
3                                 0.957094   
4                                 0.053947   

   diagnostics_Mask-interpolated_Mean  ...  \
0                           -0.157709  ...   
1                            0.065761  ...   
2                           -0.240951  ...   
3                            0.067101  ...   
4                            0.455606  ...   

   exponential_gldm_DependenceVariance  \
0                             0.167174   
1                            -0.022642   
2                             0.307832   
3                            -0.007803   
4                            -0.361869   

   exponential_gldm_GrayLevelNonUniformity  \
0                                 0.086590   
1                                -0.021777   
2                                 0.878789   
3                                 0.091376   
4                                -0.118301   

   exponential_gldm_GrayLevelVariance  exponential_gldm_HighGrayLevelEmphasis  \
0                           -0.020025                               -0.030235   
1                            0.123363                                0.117396   
2                           -0.090719                               -0.103393   
3                           -0.004511                                0.001331   
4                            0.172868                                0.202787   

   exponential_gldm_LargeDependenceEmphasis  \
0                                  0.152498   
1                                 -0.009428   
2                                  0.326512   
3                                 -0.018762   
4                                 -0.364214   

   exponential_gldm_LargeDependenceHighGrayLevelEmphasis  \
0                                          -0.029565       
1                                           0.126092       
2                                          -0.072908       
3                                           0.011745       
4                                           0.210160       

   exponential_gldm_LargeDependenceLowGrayLevelEmphasis  \
0                                           0.074005      
1                                           0.006313      
2                                           0.038977      
3                                          -0.056304      
4                                          -0.367102      

   exponential_gldm_LowGrayLevelEmphasis  \
0                               0.086142   
1                              -0.045557   
2                              -0.054606   
3                              -0.091948   
4                              -0.379090   

   exponential_gldm_SmallDependenceEmphasis  \
0                                 -0.119952   
1                                  0.037350   
2                                 -0.355241   
3                                  0.035345   
4                                  0.388277   

   exponential_gldm_SmallDependenceHighGrayLevelEmphasis  
0                                          -0.029427      
1                                           0.116168      
2                                          -0.103110      
3                                          -0.000073      
4                                           0.199694      

[5 rows x 898 columns]
import pandas as pd
correlation_matrix=pd.read_csv('correlation.csv')
# prompt: Crear un gráfico de correlación con plotly

import plotly.express as px

# Assuming 'correlation_matrix' is your DataFrame
fig = px.imshow(correlation_matrix,
                labels=dict(x="Feature", y="Feature", color="Correlation"),
                x=correlation_matrix.columns,
                y=correlation_matrix.columns,
                color_continuous_scale='RdBu',  # Use a diverging color scale
                zmin=-1, zmax=1) # Set color scale limits for better visualization

fig.update_layout(title='Correlation Matrix Heatmap',
                  width=800, height=800)
fig.show()